//
//  UIView+MBBadges.swift
//  MBBadges-Demo
//
//  Created by Trybst.Ma on 2021/5/14.
//

import Foundation
import UIKit

/// 思路来源：https://github.com/jkpang/PPBadgeView

//MARK: - 为UIView添加角标

/*
 
 特殊：UITabBarItem、UIBarButtonItem等，父级不为UIView，所以需要手动去找到相关视图
 
 
 eg.通过Xcode视图调试工具找到UITabBarItem原生Badge所在父视图
 /// 底部tabBarItem中的imageView
 /// 利用runtime获取属性和成员变量，打印结果: UITabBarItem内的成员变量类型为: @"UITabBarButton"，名字为: _view
 private var _bottomView: UIView {
     let tabBarItem = self.tabBarController?.tabBar.items?[2]
     let tabBarButton = (tabBarItem?.value(forKey: "_view") as? UIView) ?? UIView()
     for subView in tabBarButton.subviews {
         guard let superclass = subView.superclass else { return tabBarButton }
         if superclass == NSClassFromString("UIImageView") {
             return subView
         }
     }
     return tabBarButton
 }
 
 
 eg. 通过Xcode视图调试工具找到UIBarButtonItem的Badge所在父视图为:UIImageView
 /// navigationButton的View
 /// FIXME: 目前层级有变化，下次再改。。。目前单独抽离出来UIBarButtonItem分类
 private var _bottomView: UIView {
     // <UIBarButtonItem: 0x7fa10f427e40>
     let navigationButton = (self.base.value(forKey: "_view") as? UIView) ?? UIView()
     let systemVersion = (UIDevice.current.systemVersion as NSString).doubleValue
     let controlName = (systemVersion < 11.0 ? "UIImageView" : "UIButton" )
     for subView in navigationButton.subviews {
         if subView.isKind(of: NSClassFromString(controlName)!) {
             subView.layer.masksToBounds = false
             return subView
         }
     }
     return navigationButton
 }
 
 
 */

private var mb_badge_key : Void?
private var mb_badge_value_key : Void?
private var mb_badge_bgColor_key : Void?
private var mb_badge_textColor_key : Void?
private var mb_badge_font_key : Void?
private var mb_badge_padding_key : Void?
private var mb_badge_borderW_key : Void?
private var mb_badge_borderC_key : Void?
private var mb_badge_minSize_key : Void?
private var mb_badge_originX_key : Void?
private var mb_badge_originY_key : Void?

private var mb_hideBadge_Key : Void?
private var mb_animateBadge_key : Void?

//TODO: - 角标：修改边框
private var mb_badgeBorderKey : Void?

extension UIView {

    // MARK: - 角标
    fileprivate var badgeLabel: UILabel? {
        get {
            return  objc_getAssociatedObject(self, &mb_badge_key) as? UILabel
        }
        set {
            objc_setAssociatedObject(self, &mb_badge_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        
    }

    // MARK: - 角标
    /**
     * 角标：设置角标值
     */
    var badgeValue : String?  {
        get {
            return objc_getAssociatedObject(self, &mb_badge_value_key) as? String
        }
        set (badgeValue) {
                objc_setAssociatedObject(self, &mb_badge_value_key, badgeValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

            if (badgeValue?.isEmpty)!   || (badgeValue == "") || ((badgeValue == "0") && shouldHideBadgeAtZero) {
                    removeBadge()
                } else if (self.badgeLabel == nil ) {
                    self.badgeLabel  = UILabel(frame: CGRect(x: self.badgeOriginX , y: self.badgeOriginY, width: 20, height: 20))
                    self.badgeLabel?.textColor = self.badgeTextColor
                    self.badgeLabel?.backgroundColor = self.badgeBGColor
                    self.badgeLabel?.font = self.badgeFont
                    self.badgeLabel?.textAlignment = .center
                    self.badgeLabel?.layer.borderWidth = self.badgeBorderWidth
                    self.badgeLabel?.layer.borderColor = self.badgeBorderColor?.cgColor
                    badgeInit()
                    addSubview(self.badgeLabel!)
                    updateBadgeValue(animated: false)
                } else {
                    updateBadgeValue(animated: true)
                }
        }

    }

    /**
     * 角标：Border的宽度
     */
    var badgeBorderWidth: CGFloat {
        get {
            return  objc_getAssociatedObject(self, &mb_badge_borderW_key) as? CGFloat ?? 2
        }
        set {
            objc_setAssociatedObject(self, &mb_badge_borderW_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) { refreshBadge() }
        }
    }
    
    /**
     * 角标：Border的颜色
     */
    var badgeBorderColor: UIColor? {
        get {
            return objc_getAssociatedObject(self, &mb_badge_borderC_key) as? UIColor ?? .white
        }
        set {
            objc_setAssociatedObject(self, &mb_badge_borderC_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) { refreshBadge() }
        }
    }
    
    /**
     * 角标：背景的颜色
     */
    var badgeBGColor: UIColor? {
        get {
            return objc_getAssociatedObject(self, &mb_badge_bgColor_key) as? UIColor ?? .red
        }
        set {
            objc_setAssociatedObject(self, &mb_badge_bgColor_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) { refreshBadge() }
        }
    }

    /**
     * 角标：Label的字体颜色
     */
    var badgeTextColor: UIColor? {
        get {
            return objc_getAssociatedObject(self, &mb_badge_textColor_key) as? UIColor ?? .white
        }
        set {
            objc_setAssociatedObject(self, &mb_badge_textColor_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) {  refreshBadge() }
        }
    }


    /**
     * 角标：Label的字体大小
     */
    var badgeFont: UIFont? {
        get {
            return objc_getAssociatedObject(self, &mb_badge_font_key) as? UIFont ?? UIFont.systemFont(ofSize: 12)
        }
        set {
            objc_setAssociatedObject(self, &mb_badge_font_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) { refreshBadge() }
        }
    }

    /**
     *  角标：Padding 填充的像素（默认为6）
     *  可以 和 Font 一起灵活使用，实现更改角标大小的效果
     */
    var badgePadding: CGFloat {
        get {
            return  objc_getAssociatedObject(self, &mb_badge_padding_key) as? CGFloat ?? 6
        }
        set {
            objc_setAssociatedObject(self, &mb_badge_padding_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) { updateBadgeFrame() }
        }
    }

    /**
     * 角标：Label的最小尺寸
     */
    var badgeMinSize: CGFloat {
        get {
            return objc_getAssociatedObject(self, &mb_badge_minSize_key) as? CGFloat ?? 8
        }
        set {
            objc_setAssociatedObject(self, &mb_badge_minSize_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) { updateBadgeFrame() }
        }
    }

    /**
     *  角标：横坐标 OriginX
     */
    var badgeOriginX: CGFloat {
        get {
            return objc_getAssociatedObject(self, &mb_badge_originX_key) as? CGFloat ?? 0
        }
        set {
            objc_setAssociatedObject(self, &mb_badge_originX_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) {
                updateBadgeFrame()
            }
        }
    }

    /**
     * 角标：纵坐标 OriginY
     */
    var badgeOriginY: CGFloat  {
        get {
            return objc_getAssociatedObject(self, &mb_badge_originY_key) as? CGFloat ?? -6
        }
        set {
            objc_setAssociatedObject(self, &mb_badge_originY_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) {
                updateBadgeFrame()
            }
        }
    }

    /**
     * 角标为0时，隐藏角标
     */
    var shouldHideBadgeAtZero: Bool  {
        get {
            return objc_getAssociatedObject(self, &mb_hideBadge_Key) as? Bool ?? true
        }
        set {
            objc_setAssociatedObject(self, &mb_hideBadge_Key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    /**
     * 当角标变化时，设置反弹动画
     */
    var shouldAnimateBadge: Bool {
        get {
            return objc_getAssociatedObject(self, &mb_animateBadge_key) as? Bool ?? true
        }
        set {
            objc_setAssociatedObject(self, &mb_animateBadge_key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    /// 默认为原view的中心X
    fileprivate func badgeInit()  {
        if let label = self.badgeLabel {
            self.badgeOriginX = self.frame.size.width - label.frame.size.width/2
        }
        
        self.clipsToBounds = false
    }

    fileprivate func refreshBadge() {
        guard let tempLabel = self.badgeLabel else { return }
        tempLabel.textColor = self.badgeTextColor
        tempLabel.backgroundColor  = self.badgeBGColor
        tempLabel.font  = self.badgeFont
        tempLabel.layer.borderWidth = self.badgeBorderWidth
        tempLabel.layer.borderColor = self.badgeBorderColor?.cgColor
    }

    fileprivate func removeBadge() {
        UIView.animate(withDuration: 0.2, animations: {
            self.badgeLabel?.transform = CGAffineTransform.init(scaleX: 0, y: 0)
        }) { (finished: Bool) in
            self.badgeLabel?.removeFromSuperview()
            if (self.badgeLabel != nil) { self.badgeLabel = nil }
        }
    }

    fileprivate func updateBadgeValue(animated: Bool) {
        if animated && self.shouldAnimateBadge && !(self.badgeLabel?.text == self.badgeValue) {
            let animation = CABasicAnimation(keyPath: "transform.scale")
            animation.fromValue = 1.5
            animation.toValue = 1
            animation.duration = 0.2
            animation.timingFunction = CAMediaTimingFunction(controlPoints: 0.4, 1.3, 1.0, 1.0)
            self.badgeLabel?.layer.add(animation, forKey: "bounceAnimation")
        }

        var badgeValue = 0
        if let badgeStr = self.badgeValue , let value = Int(badgeStr) {
                badgeValue = value
        }
        self.badgeLabel?.text = badgeValue >= 99 ? "99+" : self.badgeValue
        self.badgeLabel?.text =  self.badgeValue
        let duration: TimeInterval = (animated && self.shouldAnimateBadge) ? 0.2 : 0
        UIView.animate(withDuration: duration, animations: {
            self.updateBadgeFrame()
        })
    }

    /// 当value有修改时，刷新frame & 添加一个小动画效果：updateBadgeValue(:)
    fileprivate  func updateBadgeFrame() {
        let expectedLabelSize: CGSize = badgeExpectedSize()
        var minHeight: CGFloat = expectedLabelSize.height
        minHeight = (minHeight < badgeMinSize) ? badgeMinSize : expectedLabelSize.height
    
        var minWidth: CGFloat = expectedLabelSize.width
        let padding = self.badgePadding
        minWidth = (minWidth < minHeight) ? minHeight : expectedLabelSize.width
        
        let badgeWidth = minWidth + padding
        let badgeHeight = minHeight + padding

        if self.badgeOriginX > self.frame.size.width {
            self.badgeOriginX = self.frame.size.width - badgeWidth/2
        }
        
        if self.badgeOriginY > self.frame.size.height {
            self.badgeOriginY = self.frame.size.height - badgeHeight/2
        }

        self.badgeLabel?.frame = CGRect(x: self.badgeOriginX, y: self.badgeOriginY, width: badgeWidth, height: badgeHeight)
        self.badgeLabel?.layer.cornerRadius = badgeHeight / 2
        self.badgeLabel?.layer.masksToBounds = true
    }

    fileprivate func badgeExpectedSize() -> CGSize {
        let frameLabel: UILabel = duplicate(self.badgeLabel)
        frameLabel.sizeToFit()
        let expectedLabelSize: CGSize = frameLabel.frame.size
        return expectedLabelSize
    }

    fileprivate func duplicate(_ labelToCopy: UILabel? ) -> UILabel {
        guard let temp = labelToCopy else { fatalError("duplicate --> fatalError") }
        let duplicateLabel = UILabel(frame: temp.frame )
        duplicateLabel.text = temp.text
        duplicateLabel.font = temp.font
        return duplicateLabel
    }
    
}
